package com.atguigu.gulimall.auth.web;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.constant.AuthConstant;
import com.atguigu.common.exception.BizCodeEnum;
import com.atguigu.common.to.MemberVo;
import com.atguigu.common.utils.R;
import com.atguigu.gulimall.auth.feign.MemberFeignService;
import com.atguigu.gulimall.auth.feign.ThirdPartyFeignService;
import com.atguigu.gulimall.auth.vo.GiteeOAuth2RespVo;
import com.atguigu.gulimall.auth.vo.RegisterVo;
import com.atguigu.gulimall.auth.vo.UserLoginVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Controller
public class AuthController {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Resource
    private ThirdPartyFeignService thirdPartyFeignService;

    @Autowired
    private MemberFeignService memberFeignService;

    @GetMapping("/sms/sendcode")
    @ResponseBody
    public R sendCode(@RequestParam("phone") String phone){
        /**
         * 1、接口防刷 TODO
         * 2、验证码校验：对于redis中已经有此验证码的，不再重新发送
         */

        //注意，这里为什么不直接判断redis中有没有这个key，而是还要去算时间?
        //因为redis中存了，不代表验证码实际就发送成功了，所以在一定间隔后应该允许重发
        //验证码发送后10分钟内有效，但60秒后就可以重新发送验证码了
        String s = redisTemplate.opsForValue().get(AuthConstant.SMS_CODE_CACHE_PREFIX + phone);
        if (StringUtils.isNotEmpty(s)){
            long l = Long.parseLong(s.split("_")[1]);
            if (System.currentTimeMillis() - l < 60*1000){
                return R.error(BizCodeEnum.SMS_CODE_EXCEPTION.getCode(),BizCodeEnum.SMS_CODE_EXCEPTION.getMsg());
            }
        }

        //String code = (""+(Math.random()*9+1)*1000000).substring(0,6)+"_"+System.currentTimeMillis();
        //验证码先写死为123456，并且不再实际发送
        String code = "123456"+"_"+System.currentTimeMillis();
        redisTemplate.opsForValue().
                set(AuthConstant.SMS_CODE_CACHE_PREFIX+phone,code,10, TimeUnit.MINUTES);
        //thirdPartyFeignService.sendCode(phone,code.substring(0,6));
        return R.ok();
    }

    @PostMapping("/register")
    public String register(@Valid RegisterVo registerVo, BindingResult bindingResult,RedirectAttributes redirectAttributes){

        if (bindingResult.hasErrors()){
            Map<String, String> errorMap = bindingResult.getFieldErrors().stream().collect(Collectors.
                    toMap(FieldError::getField, FieldError::getDefaultMessage));
            //参数校验错误，应该返回注册页面，并回显错误信息
            //这里要注意，如果直接用forward转发，则会把同样的数据重复提交？
            //model.addAttribute("errors",errorMap);
            redirectAttributes.addFlashAttribute("errors",errorMap);

            //注意，这个return "reg"和return "forward:reg.html"是不一样的，前者是直接返回渲染的页面，
            // 而后者是转发到Controller中的。controller中没有直接写，是放到WebConfig中的，
            // registry.addViewController("reg.html").setViewName("reg")默认是GET请求
            //return "reg";
            //如果用return "reg"; 点击注册，提交后如果刷新页面，则会再次提交表单，造成表单重复提交的问题，因此需要使用重定向
            //为什么刷新页面是重复提交？？因为刷新其实就是重复上一个返回了页面的请求？
            //比如这里但就是点击注册提交post请求后返回reg页面，那么刷新就会再次提交这个post请求，于是造成了表单重复提交？
            //如果用请求重定向，则刷新时上一个请求已经不是提交表单的post请求了，而是重定向的请求，所以不会有重复提交的问题

            //这里的关键是理解浏览器刷新按钮实际上是重发上一次非ajax请求,而请求转发是不改变浏览器地址栏的

            //但是，如果用redirect，数据怎么带过去呢，可以用redirectAttributes.addFlashAttribute("errors",errorMap);
            //原理，实际上是存在session里面的，只是重定向后用一次就删除,
            // 注意thymeleaf可以理解为jsp，其实也是从四大域（page，request，session，application）中取值，所以存到session中也是可以取到的
            //TODO 但是session在分布式环境下又有问题，后续处理
            return "redirect:http://auth.gulimall.com/reg.html";

            //TODO 但是浏览器提示表单重复提交的alert框，有时候出现，有时候不出现，不知道怎么回事

            //问题解决：[DOM] Input elements should have autocomplete attributes (suggested: “new-password“):如何解决？
            //解决方法： 在"type=password"的input框中添加 autocomplete=""属性；
        }

        String s = redisTemplate.opsForValue().get(AuthConstant.SMS_CODE_CACHE_PREFIX + registerVo.getPhone());
        if (StringUtils.isEmpty(s)){
            Map<String, String> errorMap = new HashMap<>();
            errorMap.put("code","验证码错误");
            redirectAttributes.addFlashAttribute("errors",errorMap);
            return "redirect:http://auth.gulimall.com/reg.html";
        }

        String redisCode = s.split("_")[0];
        if (!StringUtils.equals(redisCode,registerVo.getCode())){
            Map<String, String> errorMap = new HashMap<>();
            errorMap.put("code","验证码错误");
            redirectAttributes.addFlashAttribute("errors",errorMap);
            return "redirect:http://auth.gulimall.com/reg.html";
        }

        //到这里说明验证码对了，远程调用member的服务进行注册
        //须先删除redis中存在的验证码
        redisTemplate.delete(AuthConstant.SMS_CODE_CACHE_PREFIX+registerVo.getPhone());
        //TODO 这里明文传的账号密码怕是有问题吧
        R result = memberFeignService.register(registerVo);
        if (result.getCode() == 0){
            return "redirect:http://auth.gulimall.com/login.html"; //注册完毕重定向至登录页
        }else {
            Map<String, String> errorMap = new HashMap<>();
            errorMap.put("msg",result.getData(new TypeReference<String>(){},"msg"));
            redirectAttributes.addFlashAttribute("errors",errorMap);
            return "redirect:http://auth.gulimall.com/reg.html";
        }

    }

    @GetMapping("/login.html")
    public String loginPage(HttpSession session){
        Object attribute = session.getAttribute(AuthConstant.LOGIN_USER);
        if (attribute != null){
            return "redirect:http://gulimall.com";
        }
        return "login";
    }

    @PostMapping("/login")
    public String login(UserLoginVo userLoginVo,RedirectAttributes redirectAttributes,HttpSession session){

        R login = memberFeignService.login(userLoginVo);
        if (login.getCode() == 0){
            MemberVo memberVo = login.getData(new TypeReference<MemberVo>() {
            });
            session.setAttribute(AuthConstant.LOGIN_USER,memberVo);
            return "redirect:http://gulimall.com";
        }else {
            HashMap<String, String> errorMap = new HashMap<>();
            errorMap.put("msg",login.getData(new TypeReference<String>(){},"msg"));
            redirectAttributes.addFlashAttribute("errors",errorMap);
            return "redirect:http://auth.gulimall.com/login.html";
        }
    }

    @GetMapping("/success")
    public String success(@RequestParam("code") String code, RedirectAttributes redirectAttributes, HttpSession session){

        HttpURLConnection conn = null;
        try {
            URL url = new URL("https://gitee.com/oauth/token");
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);

            // 设置请求头，例如内容类型
            conn.setRequestProperty("Content-Type", "application/json");

            HashMap<String, String> paramMap = new HashMap<>();
            paramMap.put("grant_type","authorization_code");
            paramMap.put("code",code);
            paramMap.put("client_id",AuthConstant.GITEE_CLIENT_ID);
            paramMap.put("redirect_uri",AuthConstant.REDIRECT_URI);
            paramMap.put("client_secret",AuthConstant.GITEE_CLIENT_SECRET);

            // 写入请求体
            String jsonInputString = JSON.toJSONString(paramMap);
            try(OutputStream os = conn.getOutputStream()) {
                byte[] input = jsonInputString.getBytes();
                os.write(input, 0, input.length);
            }

            // 获取响应码以检查成功与否
            int responseCode = conn.getResponseCode();

            // 读取响应体
            if (responseCode == 200){
                InputStream inputStream = conn.getInputStream();
                InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"utf8");
                char[] chars = new char[1024];
                int pos = 0;
                String result = "";
                while((pos = inputStreamReader.read(chars)) > 0){
                    result = result + new String(chars,0,pos);
                }
                GiteeOAuth2RespVo giteeOAuth2RespVo = JSON.parseObject(result, GiteeOAuth2RespVo.class);

                //用GiteeOAuth2RespVo再去调用member的远程服务实现注册或登录
                //接收到的事一个member对象，说明登录成功了，返回首页
                R r = memberFeignService.auth2Login(giteeOAuth2RespVo);
                if (r.getCode() == 0){
//                    MemberVo memberVo = r.getData(new TypeReference<MemberVo>(){});
//                    redirectAttributes.addFlashAttribute("userInfo",memberVo);
                    session.setAttribute(AuthConstant.LOGIN_USER,r.getData(new TypeReference<MemberVo>(){}));
                    return "redirect:http://gulimall.com/index.html";
                }else {
                    return "redirect:http://auth.gulimall.com/login.html";
                }
            }else{
                log.error("responseCode:"+responseCode);
                //登录不成功，再返回登录页面
                return "redirect:http://auth.gulimall.com/login.html";
            }

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (conn != null){
                conn.disconnect();
            }
        }
        return "redirect:http://auth.gulimall.com/login.html";
    }
}
